﻿using System;
using System.Collections.Generic;
using System.Text;
using IronRuby.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using IronRuby.Runtime;
using System.IO;

namespace MapMaker.Engine.Model.GameMaker
{
    public class Manager
    {
        #region static setup
        private static Dictionary<Database, string> FILE_NAMES = new Dictionary<Database, string>();
        static Manager()
        {
            FILE_NAMES.Add(Database.Actors, "Actors");
            FILE_NAMES.Add(Database.Animations, "Animations");
            FILE_NAMES.Add(Database.Armors, "Armors");
            FILE_NAMES.Add(Database.Classes, "Classes");
            FILE_NAMES.Add(Database.CommonEvents, "CommonEvents");
            FILE_NAMES.Add(Database.Enemies, "Enemies");
            FILE_NAMES.Add(Database.Items, "Items");
            FILE_NAMES.Add(Database.Maps, "Map");
            FILE_NAMES.Add(Database.MapInfos, "MapInfos");
            FILE_NAMES.Add(Database.Scripts, "Scripts");
            FILE_NAMES.Add(Database.Skills, "Skills");
            FILE_NAMES.Add(Database.States, "States");
            FILE_NAMES.Add(Database.System, "System");
            FILE_NAMES.Add(Database.Tilesets, "Tilesets");
            FILE_NAMES.Add(Database.Troops, "Troops");
            FILE_NAMES.Add(Database.Weapons, "Weapons");
        }
        #endregion


        private ScriptRuntimeSetup RuntimeSetup { get; set; }
        private LanguageSetup RubySetup { get; set; }
        public ScriptEngine Engine { get; private set; }
        private ScriptScope ScriptScope { get; set; }
        private ScriptSource Source { get; set; }
        private IronRubyErrors Error { get; set; }
        private dynamic RubyEngine { get; set; }

        public Manager()
        {
            RuntimeSetup = new ScriptRuntimeSetup();
            RuntimeSetup.DebugMode = true;
            RubySetup = IronRuby.Ruby.CreateRubySetup();
            RubySetup.ExceptionDetail = true;
            RuntimeSetup.LanguageSetups.Add(RubySetup);
            Engine = new ScriptRuntime(RuntimeSetup).GetEngine("IronRuby");
            ScriptScope = Engine.CreateScope();
            Source = Engine.CreateScriptSourceFromFile(@"GameMaker.rb");
            Error = new IronRubyErrors();
            Source.Compile();
        }

        public string Path { get; set; }

        public void Save(Database file, Func<dynamic> acquire)
        {
            if (RubyEngine == null) Initalize();
            if (file == Database.Maps)
            {
                SaveMaps(acquire);
                return;
            }
            else
            { 

            }        
        }

        private void SaveMaps(Func<dynamic> acquire)
        {
        }

        public void Load(Database file, Action<dynamic> process)
        {
            if (RubyEngine == null) Initalize();
            if (file == Database.Maps)
            {
                LoadMaps(FILE_NAMES[file], process);
                return;
            }
            else
            {
                Load(FILE_NAMES[file], process);
            }     
        }

        private void Load(string file, Action<dynamic> process)
        {
            var item = Engine.Operations.InvokeMember(RubyEngine, "load", System.IO.Path.Combine(Path, string.Concat(file,".rvdata2")));
            process(item);
        }

        private void LoadMaps(string file, Action<dynamic> process)
        {
            DirectoryInfo dir = new DirectoryInfo(Path);            
            foreach (FileInfo f in dir.GetFiles(string.Concat(file, "*.rvdata2"), SearchOption.TopDirectoryOnly))
            {
                if (f.Name.ToLower().Contains("info")) continue;
                var item = Engine.Operations.InvokeMember(RubyEngine, "load", f.FullName);
                process(item);
            }

        }

        private void Initalize()
        {
            try
            {
            Source.Execute(ScriptScope);
            var klass = Engine.Runtime.Globals.GetVariable("PDQ");
            RubyEngine = Engine.Operations.CreateInstance(klass);
            }
            catch (Exception e)
            {
                int line = GetExecutableLine(e);
                throw e;
            }
            finally
            {
            }
        }
    

        static int GetExecutableLine(Exception e)
        {
            if (e is SyntaxErrorException)
                return (e as SyntaxErrorException).Line;
            else 
            {
                RubyExceptionData red = RubyExceptionData.GetInstance(e);
                int line = 0;
                // first line of backtrace is "<bogus file name>:<line number>"
                int.TryParse(red.Backtrace[0].ToString().Split(':')[1], out line);
                return line;
            }
        }
    }
}
